﻿#include "DXManager.h"
#include "../Settings.h"
#include "../Program.h"
#include "../Forms/Client.CMain.h"
#include "../MirScenes/GameScene.h"

using namespace Client::MirControls;
using namespace Client::MirScenes;
using namespace SlimDX;
using namespace SlimDX::Direct3D9;

namespace Client::MirGraphics
{

std::vector<MImage*> DXManager::TextureList;
std::vector<MirControl*> DXManager::ControlList;
DXManager::Device *Device;
DXManager::Sprite *Sprite;
Sprite *DXManager::TextSprite;
DXManager::Line *Line;
Surface *DXManager::CurrentSurface;
Surface *DXManager::MainSurface;
PresentParameters *DXManager::Parameters;
bool DXManager::DeviceLost = false;
float DXManager::Opacity = 1.0F;
bool DXManager::Blending = false;
float DXManager::BlendingRate = 0;
BlendMode DXManager::BlendingMode = static_cast<BlendMode>(0);
Texture *DXManager::RadarTexture;
std::vector<Texture*> DXManager::Lights;
Texture *DXManager::PoisonDotBackground;
Texture *DXManager::FloorTexture;
Texture *DXManager::LightTexture;
Surface *DXManager::FloorSurface;
Surface *DXManager::LightSurface;
PixelShader *DXManager::GrayScalePixelShader;
PixelShader *DXManager::NormalPixelShader;
PixelShader *DXManager::MagicPixelShader;
PixelShader *DXManager::ShadowPixelShader;
bool DXManager::GrayScale = false;
std::vector<Point*> DXManager::LightSizes =
{
    new Point(125,95),
    new Point(205,156),
    new Point(285,217),
    new Point(365,277),
    new Point(445,338),
    new Point(525,399),
    new Point(605,460),
    new Point(685,521),
    new Point(765,581),
    new Point(845,642),
    new Point(925,703)
};

    void DXManager::Create()
    {
        Parameters = new PresentParameters();
        Parameters->BackBufferFormat = Format::X8R8G8B8;
        Parameters->PresentFlags = PresentFlags::LockableBackBuffer;
        Parameters->BackBufferWidth = Settings::ScreenWidth;
        Parameters->BackBufferHeight = Settings::ScreenHeight;
        Parameters->SwapEffect = SwapEffect::Discard;
        Parameters->PresentationInterval = Settings::FPSCap ? PresentInterval::One : PresentInterval::Immediate;
        Parameters->Windowed = !Settings::FullScreen;


        Direct3D *d3d = new Direct3D();

        Capabilities *devCaps = d3d->GetDeviceCaps(0, DeviceType::Hardware);
        DeviceType *devType = DeviceType::Reference;
        CreateFlags *devFlags = CreateFlags::HardwareVertexProcessing;

        if (devCaps->VertexShaderVersion.Major >= 2 && devCaps->PixelShaderVersion.Major >= 2)
        {
            devType = DeviceType::Hardware;
        }

        if ((devCaps->DeviceCaps & DeviceCaps::HWTransformAndLight) != 0)
        {
            devFlags = CreateFlags::HardwareVertexProcessing;
        }


        if ((devCaps->DeviceCaps & DeviceCaps::PureDevice) != 0)
        {
            devFlags |= CreateFlags::PureDevice;
        }


        Device = new Device(d3d, d3d->Adapters.DefaultAdapter.Adapter, devType, Program::Form->Handle, devFlags, Parameters);

        Device->SetDialogBoxMode(true);

        LoadTextures();
        LoadPixelsShaders();

//C# TO C++ CONVERTER TODO TASK: A 'delete d3d' statement was not added since d3d was passed to a method or constructor. Handle memory management manually.
    }

    void DXManager::SetSurface(Surface *surface)
    {
        if (CurrentSurface == surface)
        {
            return;
        }

        Sprite->Flush();
        CurrentSurface = surface;
        Device->SetRenderTarget(0, surface);
    }

    void DXManager::SetGrayscale(bool value)
    {
        GrayScale = value;

        if (value == true)
        {
            if (Device->PixelShader == GrayScalePixelShader)
            {
                return;
            }
            Sprite->Flush();
            Device->PixelShader = GrayScalePixelShader;
        }
        else
        {
            if (Device->PixelShader == nullptr)
            {
                return;
            }
            Sprite->Flush();
            Device->PixelShader = nullptr;
        }
    }

    void DXManager::AttemptReset()
    {
        try
        {
            Result *result = DXManager::Device->TestCooperativeLevel();

            if (result->Code == ResultCode::DeviceLost::Code)
            {
                return;
            }

            if (result->Code == ResultCode::DeviceNotReset::Code)
            {
                DXManager::ResetDevice();
                return;
            }

            if (result->Code != ResultCode::Success::Code)
            {
                return;
            }

            DXManager::DeviceLost = false;
        }
        catch (...)
        {
        }
    }

    void DXManager::ResetDevice()
    {
        DXManager::CleanUp();
        DXManager::DeviceLost = true;

        if (DXManager::Parameters == nullptr)
        {
            return;
        }

        Size *clientSize = Program::Form->ClientSize;

        if (clientSize->Width == 0 || clientSize->Height == 0)
        {
            return;
        }

        DXManager::Parameters->Windowed = !Settings::FullScreen;
        DXManager::Parameters->BackBufferWidth = clientSize->Width;
        DXManager::Parameters->BackBufferHeight = clientSize->Height;
        DXManager::Parameters->PresentationInterval = Settings::FPSCap ? PresentInterval::Default : PresentInterval::Immediate;
        DXManager::Device->Reset(DXManager::Parameters);

        DXManager::LoadTextures();
    }

    void DXManager::AttemptRecovery()
    {
        try
        {
            Sprite->End();
        }
        catch (...)
        {
        }

        try
        {
            Device->EndScene();
        }
        catch (...)
        {
        }

        try
        {
            MainSurface = Device->GetBackBuffer(0, 0);
            CurrentSurface = MainSurface;
            Device->SetRenderTarget(0, MainSurface);
        }
        catch (...)
        {
        }
    }

    void DXManager::SetOpacity(float opacity)
    {
        if (Opacity == opacity)
        {
            return;
        }

        Sprite->Flush();
        Device->SetRenderState(RenderState::AlphaBlendEnable, true);
        if (opacity >= 1 || opacity < 0)
        {
            Device->SetRenderState(RenderState::SourceBlend, SlimDX::Direct3D9::Blend::SourceAlpha);
            Device->SetRenderState(RenderState::DestinationBlend, SlimDX::Direct3D9::Blend::InverseSourceAlpha);
            Device->SetRenderState(RenderState::SourceBlendAlpha, SlimDX::Direct3D9::Blend::One);
            Device->SetRenderState(RenderState::BlendFactor, Color::FromArgb(255, 255, 255, 255).ToArgb());
        }
        else
        {
            Device->SetRenderState(RenderState::SourceBlend, SlimDX::Direct3D9::Blend::BlendFactor);
            Device->SetRenderState(RenderState::DestinationBlend, SlimDX::Direct3D9::Blend::InverseBlendFactor);
            Device->SetRenderState(RenderState::SourceBlendAlpha, SlimDX::Direct3D9::Blend::SourceAlpha);
            Device->SetRenderState(RenderState::BlendFactor, Color::FromArgb(static_cast<unsigned char>(255 * opacity), static_cast<unsigned char>(255 * opacity), static_cast<unsigned char>(255 * opacity), static_cast<unsigned char>(255 * opacity)).ToArgb());
        }
        Opacity = opacity;
        Sprite->Flush();
    }

    void DXManager::SetBlend(bool value, float rate, BlendMode mode)
    {
        if (value == Blending && BlendingRate == rate && BlendingMode == mode)
        {
            return;
        }

        Blending = value;
        BlendingRate = rate;
        BlendingMode = mode;

        Sprite->Flush();

        Sprite->End();

        if (Blending)
        {
            Sprite->Begin(SpriteFlags::DoNotSaveState);
            Device->SetRenderState(RenderState::AlphaBlendEnable, true);

            switch (BlendingMode)
            {
                case BlendMode::INVLIGHT:
                    Device->SetRenderState(RenderState::BlendOperation, BlendOperation->Add);
                    Device->SetRenderState(RenderState::SourceBlend, SlimDX::Direct3D9::Blend::BlendFactor);
                    Device->SetRenderState(RenderState::DestinationBlend, SlimDX::Direct3D9::Blend::InverseSourceColor);
                    break;
                default:
                    Device->SetRenderState(RenderState::SourceBlend, SlimDX::Direct3D9::Blend::SourceAlpha);
                    Device->SetRenderState(RenderState::DestinationBlend, SlimDX::Direct3D9::Blend::One);
                    break;
            }

            Device->SetRenderState(RenderState::BlendFactor, Color::FromArgb(static_cast<unsigned char>(255 * BlendingRate), static_cast<unsigned char>(255 * BlendingRate), static_cast<unsigned char>(255 * BlendingRate), static_cast<unsigned char>(255 * BlendingRate)).ToArgb());
        }
        else
        {
            Sprite->Begin(SpriteFlags::AlphaBlend);
        }

        Device->SetRenderTarget(0, CurrentSurface);
    }

    void DXManager::SetNormal(float blend, Color *tintcolor)
    {
        if (Device->PixelShader == NormalPixelShader)
        {
            return;
        }

        Sprite->Flush();
        Device->PixelShader = NormalPixelShader;
        Device->SetPixelShaderConstant(0, std::vector<Vector4*> {new Vector4(1.0F, 1.0F, 1.0F, blend)});
        Device->SetPixelShaderConstant(1, std::vector<Vector4*> {new Vector4(tintcolor->R / 255, tintcolor->G / 255, tintcolor->B / 255, 1.0F)});
        Sprite->Flush();
    }

    void DXManager::SetGrayscale(float blend, Color *tintcolor)
    {
        if (Device->PixelShader == GrayScalePixelShader)
        {
            return;
        }

        Sprite->Flush();
        Device->PixelShader = GrayScalePixelShader;
        Device->SetPixelShaderConstant(0, std::vector<Vector4*> {new Vector4(1.0F, 1.0F, 1.0F, blend)});
        Device->SetPixelShaderConstant(1, std::vector<Vector4*> {new Vector4(tintcolor->R / 255, tintcolor->G / 255, tintcolor->B / 255, 1.0F)});
        Sprite->Flush();
    }

    void DXManager::SetBlendMagic(float blend, Color *tintcolor)
    {
        if (Device->PixelShader == MagicPixelShader || MagicPixelShader == nullptr)
        {
            return;
        }

        Sprite->Flush();
        Device->PixelShader = MagicPixelShader;
        Device->SetPixelShaderConstant(0, std::vector<Vector4*> {new Vector4(1.0F, 1.0F, 1.0F, blend)});
        Device->SetPixelShaderConstant(1, std::vector<Vector4*> {new Vector4(tintcolor->R / 255, tintcolor->G / 255, tintcolor->B / 255, 1.0F)});
        Sprite->Flush();
    }

    void DXManager::Clean()
    {
        for (int i = TextureList.size() - 1; i >= 0; i--)
        {
            MImage *m = TextureList[i];

            if (m == nullptr)
            {
                TextureList.erase(TextureList.begin() + i);
                continue;
            }

            if (CMain::Time <= m->CleanTime)
            {
                continue;
            }

            m->DisposeTexture();
        }

        for (int i = ControlList.size() - 1; i >= 0; i--)
        {
            MirControl *c = ControlList[i];

            if (c == nullptr)
            {
                ControlList.erase(ControlList.begin() + i);
                continue;
            }

            if (CMain::Time <= c->CleanTime)
            {
                continue;
            }

            c->DisposeTexture();
        }
    }

    void DXManager::CleanUp()
    {
        if (Sprite != nullptr)
        {
            if (!Sprite->Disposed)
            {
                Sprite->Dispose();
            }

            Sprite = nullptr;
        }

        if (Line != nullptr)
        {
            if (!Line->Disposed)
            {
                Line->Dispose();
            }

            Line = nullptr;
        }

        if (CurrentSurface != nullptr)
        {
            if (!CurrentSurface->Disposed)
            {
                CurrentSurface->Dispose();
            }

            CurrentSurface = nullptr;
        }

        if (PoisonDotBackground != nullptr)
        {
            if (!PoisonDotBackground->Disposed)
            {
                PoisonDotBackground->Dispose();
            }

            PoisonDotBackground = nullptr;
        }

        if (RadarTexture != nullptr)
        {
            if (!RadarTexture->Disposed)
            {
                RadarTexture->Dispose();
            }

            RadarTexture = nullptr;
        }

        if (FloorTexture != nullptr)
        {
            if (!FloorTexture->Disposed)
            {
                FloorTexture->Dispose();
            }

            DXManager::FloorTexture = nullptr;
            GameScene::Scene->MapControl->FloorValid = false;

            if (DXManager::FloorSurface != nullptr && !DXManager::FloorSurface->Disposed)
            {
                DXManager::FloorSurface->Dispose();
            }

            DXManager::FloorSurface = nullptr;
        }

        if (LightTexture != nullptr)
        {
            if (!LightTexture->Disposed)
            {
                LightTexture->Dispose();
            }

            DXManager::LightTexture = nullptr;

            if (DXManager::LightSurface != nullptr && !DXManager::LightSurface->Disposed)
            {
                DXManager::LightSurface->Dispose();
            }

            DXManager::LightSurface = nullptr;
        }

        if (!Lights.empty())
        {
            for (int i = 0; i < Lights.size(); i++)
            {
                if (!Lights[i]->Disposed)
                {
                    Lights[i]->Dispose();
                }
            }
            Lights.clear();
        }

        for (int i = TextureList.size() - 1; i >= 0; i--)
        {
            MImage *m = TextureList[i];

            if (m == nullptr)
            {
                continue;
            }

            m->DisposeTexture();
        }
        TextureList.clear();


        for (int i = ControlList.size() - 1; i >= 0; i--)
        {
            MirControl *c = ControlList[i];

            if (c == nullptr)
            {
                continue;
            }

            c->DisposeTexture();
        }
        ControlList.clear();
    }
}
